/*
 * @(#) $Header$
 *
 * Copyright (C) 2006  Daniel Léonard
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package ca.forklabs.baselib.io;

import javax.swing.JTextArea;
import javax.swing.SwingUtilities;

import java.io.IOException;

/**
 * Class {@code JTextAreaWriter} writes into a {@link JTextArea}. By default,
 * the caret will be set at the end of the text after a write.
 *
 * @author   <a href="mailto:forklabs at gmail.com?subject=ca.forklabs.baselib.io.JTextAreaWriter">Daniel Léonard</a>
 * @version $Revision$
 */
public class JTextAreaWriter extends AbstractWriter {

//---------------------------
// Instance variables
//---------------------------

   /** The sink. */
   private JTextArea sink;

   /** Determines if the caret should go at the end of the text. */
   private boolean go_at_end = true;


//---------------------------
// Constructors
//---------------------------

   /**
    * Constructor.
    * @param   sink   the destination of all the text.
    */
   public JTextAreaWriter(JTextArea sink) {
      this.setSink(sink);
      }

   /**
    * Constructor. The critical sections will synchronized on the given lock.
    * @param   sink   the destination of all the text.
    * @param   lock   the lock object.
    */
   public JTextAreaWriter(JTextArea sink, Object lock) {
      super(lock);
      this.setSink(sink);
      }


//---------------------------
// Accessors and mutators
//---------------------------

   /**
    * Changes the sink.
    * @param   sink   the new sink.
    */
   protected void setSink(JTextArea sink) {
      this.sink = sink;
      }

   /**
    * Gets the sink.
    * @return   the sink.
    */
   protected JTextArea getSink() {
      return this.sink;
      }

   /**
    * Changes the behaviour of the caret. If set to {@code true}, the caret will
    * be moved at the end of the text after a write. If set to {@code false},
    * the caret will not be moved. The default is {@code true}.
    * @param   state   if the caret goes at the end.
    */
   public void setGoAtEnd(boolean state) {
      this.go_at_end = state;
      }

   /**
    * Determines if the caret should go at the end of the text after a write.
    * @return   {@code true} if the caret should go at the end after a write,
    *           {@code false} otherwise.
    */
   public boolean goesAtEnd() {
      return this.go_at_end;
      }


//---------------------------
// Overridden methods from java.io.Writer
//---------------------------

   /**
    * Write a portion of an array of characters into the sink.
    * @param   cbuf  array of characters.
    * @param   off   offset from which to start writing characters.
    * @param   len   number of characters to write.
    * @exception   IOException   if the stream is closed.
    */
   @Override
   public void write(char[] cbuf, int off, int len) throws IOException {
      Object the_lock = this.getLock();
      synchronized (the_lock) {
         this.ensureWriterOpened();

         final JTextArea text_area = this.getSink();
         final String string = new String(cbuf, off, len);
         final boolean goes_at_end = this.goesAtEnd();
      // let us not forget that SWING components should
      // always be accessed from the event dispatching thread
         SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
               text_area.append(string);
               if (goes_at_end) {
                  String text = text_area.getText();
                  int position = text.length();
                  text_area.setCaretPosition(position);
                  }
               }
            });
         }
      }

   /**
    * Does nothing, every write is automatically appended to the sink.
    * @exception   IOException   if the stream is closed.
    */
   @Override
   @SuppressWarnings("hiding")
   public void flush() throws IOException {
      Object lock = this.getLock();
      synchronized (lock) {
         this.ensureWriterOpened();
         }
      }

   }
